package sceneObjects;

import utils.*;
import main.Intersection;
import main.Parseable;
import main.Parser;

public class CircularPlane extends abstractSceneObject implements sceneObject, Parseable {
    private Point3D centerPoint;
    private Vector normal;
    private double radius = 0;

    @Override
    public boolean setParameter(String name, String[] args) {
	if (super.setParameter(name, args)) {
	    return true;
	} else if (name.equals("center")) {
	    if (args.length != 3) {
		System.out.println("bad parameters");
		return false;
	    }
	    centerPoint = new Point3D(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("normal")) {
	    if (args.length != 3) {
		System.out.println("bad parameters");
		return false;
	    }
	    normal = new Vector(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("radius")) {
	    if (args.length != 1) {
		System.out.println("bad parameters");
		return false;
	    }
	    radius = Double.parseDouble(args[0]);
	    return true;
	}

	// We didn't find any parameter
	return false;
    }

    public CircularPlane(Point3D p0, Vector normal, double radius) {
	this.centerPoint = new Point3D(p0);
	this.normal = new Vector(normal).normalize();
	this.radius = radius;

	finalizeObject();
    }

    public CircularPlane() {

    }

    @Override
    public boolean finalizeObject() {
	if (!super.finalizeObject()) {
	    return false;
	}
	// Checks if the 3 points exists
	if (centerPoint == null || normal == null || radius <= 0) {
	    System.out.println("Error: one or more parameters are missing");
	    return false;
	}

	normal = normal.normalize();

	return true;
    }

    @Override
    public boolean isCamera() {
	return false;
    }

    public boolean isLight() {
	return false;
    }

    @Override
    public Intersection getIntersect(Ray ray) {

	Point3D pPlane = ray.getPlaneIntersection(new Plane(centerPoint, normal));

	if (pPlane == null) {
	    return null;
	}

	if (new Vector(centerPoint, pPlane).getNorma() <= radius) {
	    return new Intersection(pPlane.addVector(ray.getDirection().scale(-0.0001F)), this);
	} else {
	    return null;
	}
    }

    public Vector getNormalAtPoint(Intersection intersect) {
	return new Vector(normal);
    }

    public Point3D getParameterization(Point3D p) {
	Vector v1 = normal;

	double distance = centerPoint.distance(p);

	Vector axis = new Vector(1, 0, 0); // Take the X axis
	if (Math.abs(axis.dotProduct(v1)) == 1) { // Z is actually the X axis, therefore pick Y
	    axis = new Vector(0, 1, 0);
	}
	Vector v2 = axis.crossProduct(v1).normalize();

	Vector planeVector = new Vector(centerPoint, p).normalize();
	double u = distance / radius;
	double v = Math.acos(v2.dotProduct(planeVector));

	Vector normalChecker = planeVector.crossProduct(v2).normalize();
	if (normalChecker.dotProduct(normal) < 0) {
	    v = Math.PI * 2 - v;
	}
	v = v / (2 * Math.PI);

	u = Math.min(u, 0.99999);
	v = Math.min(v, 0.99999);

	return new Point3D(u, v, 0);
    }

    public Point3D getParameterization(Intersection intersect) {
	return getParameterization(intersect.getPoint());
    }

}
